import 'package:flutter/material.dart';

class FractionallyConstrainedBox extends StatelessWidget {
  const FractionallyConstrainedBox({
    Key? key,
    this.minWidthFactor,
    this.minHeightFactor,
    this.widthFactor,
    this.heightFactor,
    this.maxWidthFactor,
    this.maxHeightFactor,
    required this.child,
  })  : assert(minWidthFactor == null || minWidthFactor >= 0.0),
        assert(minHeightFactor == null || minHeightFactor >= 0.0),
        assert(widthFactor == null || widthFactor >= 0.0),
        assert(heightFactor == null || heightFactor >= 0.0),
        assert(maxWidthFactor == null || maxWidthFactor >= 0.0),
        assert(maxHeightFactor == null || maxHeightFactor >= 0.0),
        super(key: key);

  final double? minWidthFactor;
  final double? minHeightFactor;
  final double? widthFactor;
  final double? heightFactor;
  final double? maxWidthFactor;
  final double? maxHeightFactor;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: _buildChild);
  }

  Widget _buildChild(BuildContext context, BoxConstraints constraints) {
    return ConstrainedBox(
      constraints: constraints.factor(
        minWidthFactor: minWidthFactor,
        minHeightFactor: minHeightFactor,
        widthFactor: widthFactor,
        heightFactor: heightFactor,
        maxWidthFactor: maxWidthFactor,
        maxHeightFactor: maxHeightFactor,
      ),
      child: child,
    );
  }
}

extension AppBoxConstraintsExt on BoxConstraints {
  BoxConstraints factor({
    double? minWidthFactor,
    double? minHeightFactor,
    double? widthFactor,
    double? heightFactor,
    double? maxWidthFactor,
    double? maxHeightFactor,
  }) {
    return BoxConstraints(
      minWidth: minWidthFactor != null ? maxWidth * minWidthFactor : minWidth,
      maxWidth: maxWidthFactor != null ? maxWidth * maxWidthFactor : maxWidth,
      minHeight:
          minHeightFactor != null ? maxHeight * minHeightFactor : minHeight,
      maxHeight:
          maxHeightFactor != null ? maxHeight * maxHeightFactor : maxHeight,
    ).normalize().tighten(
          width: maxWidth.isFinite && widthFactor != null
              ? maxWidth * widthFactor
              : null,
          height: maxHeight.isFinite && heightFactor != null
              ? maxHeight * heightFactor
              : null,
        );
  }
}
